  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="generator" content="pandoc" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>GTK 4 tutorial</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre{overflow: visible;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::after
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {   }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span.al { color: #ff0000; font-weight: bold; } /* Alert */
      code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
      code span.at { color: #7d9029; } /* Attribute */
      code span.bn { color: #40a070; } /* BaseN */
      code span.bu { } /* BuiltIn */
      code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
      code span.ch { color: #4070a0; } /* Char */
      code span.cn { color: #880000; } /* Constant */
      code span.co { color: #60a0b0; font-style: italic; } /* Comment */
      code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
      code span.do { color: #ba2121; font-style: italic; } /* Documentation */
      code span.dt { color: #902000; } /* DataType */
      code span.dv { color: #40a070; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #40a070; } /* Float */
      code span.fu { color: #06287e; } /* Function */
      code span.im { } /* Import */
      code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
      code span.kw { color: #007020; font-weight: bold; } /* Keyword */
      code span.op { color: #666666; } /* Operator */
      code span.ot { color: #007020; } /* Other */
      code span.pp { color: #bc7a00; } /* Preprocessor */
      code span.sc { color: #4070a0; } /* SpecialChar */
      code span.ss { color: #bb6688; } /* SpecialString */
      code span.st { color: #4070a0; } /* String */
      code span.va { color: #19177c; } /* Variable */
      code span.vs { color: #4070a0; } /* VerbatimString */
      code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
      div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
      th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
      td {padding: 2px 6px; border: 1px solid;}
      img {display: block; margin-left: auto; margin-right: auto;}
      figcaption {text-align: center;}
    </style>
  </head>
  <body style="padding-top: 70px;">
    <div class="container">
    <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand">Gtk4 tutorial</span>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec12.html">Prev: section12</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec14.html">Next: section14</a>
</li>

          </ul>
        </div>
      </div>
    </nav>
<h1 id="functions-in-tfetextview">Functions in TfeTextView</h1>
<p>TfeTextView functions are described in this section.</p>
<h2 id="tfetextview.h">tfetextview.h</h2>
<p>The header file <code>tfetextview.h</code> provides:</p>
<ul>
<li>The type of TfeTextView, which is
<code>TFE_TYPE_TEXT_VIEW</code>.</li>
<li>The expansion of <code>G_DECLARE_FINAL_TYPE</code> includes some
useful macros.</li>
<li>Constants for the <code>open-response</code> signal is
defined..</li>
<li>Public functions of <code>tfetextview.c</code> are declared.</li>
</ul>
<p>Therefore, Any programs use TfeTextView needs to include
<code>tfetextview.h</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview//tfetextview.h @@@</p>
<ul>
<li>1,2,35: Thanks to these three lines, the following lines are
included only once. You can use <code>#pragma once</code> instead of
them. It is non-standard but widely used.</li>
<li>4: Includes gtk4 header files. The header file <code>gtk4</code>
also has the same mechanism to avoid including it multiple times.</li>
<li>6-7: These two lines define TfeTextView type, its class structure
and some useful macros.
<ul>
<li><code>TfeTextView</code> and <code>TfeTextViewClass</code> are
declared as typedef of C structures.</li>
<li>You need to define a structure <code>_TfeTextView</code> later.</li>
<li>The class structure <code>_TfeTextViewClass</code> is defined here.
You don’t need to define it by yourself.</li>
<li>Convenience functions <code>TFE_TEXT_VIEW ()</code> for casting and
<code>TFE_IS_TEXT_VIEW</code> for type check are defined.</li>
</ul></li>
<li>9-15: A definition of the value of the parameter of “open-response”
signal.</li>
<li>17-33: Declarations of public functions on TfeTextView.</li>
</ul>
<h2 id="instance-creation-functions">Instance creation Functions</h2>
<p>A TfeTextView instance is created with <code>tfe_text_view_new</code>
or <code>tfe_text_view_new_with_file</code>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
<p><code>tfe_text_view_new</code> just creates a new TfeTextView
instance and returns the pointer to the new instance.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div>
<p><code>tfe_text_view_new_with_file</code> is given a Gfile object as
an argument and it loads the file into the GtkTextBuffer instance, then
returns the pointer to the new instance. If an error occurs during the
creation process, NULL is returned.</p>
<p>Each function is defined as follows.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
@@@</p>
<ul>
<li>23-25: <code>tfe_text_view_new</code> function. Just returns the
value from the function <code>g_object_new</code> but casts it to the
pointer to GtkWidget. The function <code>g_object_new</code> creates any
instances of its descendant class. The arguments are the type of the
class, property list and NULL. Null is the end mark of the property
list. TfeTextView “wrap-mode” property has GTK_WRAP_WORD_CHAR as the
default value.</li>
<li>1-21: <code>tfe_text_view_new_with_file</code> function.</li>
<li>3: <code>g_return_val_if_fail</code> is described in <a
href="https://docs.gtk.org/glib/func.return_val_if_fail.html">GLib API
Reference – g_return_val_if_fail</a>. And also <a
href="https://docs.gtk.org/glib/logging.html">GLib API Reference –
Message Logging</a>. It tests whether the argument <code>file</code> is
a pointer to GFile. If it’s true, then the program goes on to the next
line. If it’s false, then it returns NULL (the second argument)
immediately. And at the same time it logs out the error message (usually
the log is outputted to stderr or stdout). This function is used to
check the programmer’s error. If an error occurs, the solution is
usually to change the (caller) program and fix the bug. You need to
distinguish programmer’s errors and runtime errors. You shouldn’t use
this function to find runtime errors.</li>
<li>10-11: If an error occurs when reading the file, then the function
returns NULL.</li>
<li>13: Calls the function <code>tfe_text_view_new</code>. The function
creates TfeTextView instance and returns the pointer to the instance. If
an error happens in <code>tfe_text_view_new</code>, it returns
NULL.</li>
<li>14: Gets the pointer to GtkTextBuffer corresponds to
<code>tv</code>. The pointer is assigned to <code>tb</code></li>
<li>15: Assigns the contents read from the file to GtkTextBuffer pointed
by <code>tb</code>.</li>
<li>16: Duplicates <code>file</code> and sets <code>tv-&gt;file</code>
to point it.</li>
<li>17: The function
<code>gtk_text_buffer_set_modified (tb, FALSE)</code> sets the
modification flag of <code>tb</code> to FALSE. The modification flag
indicates that the contents of the buffer has been modified. It is used
when the contents are saved. If the modification flag is FALSE, it
doesn’t need to save the contents.</li>
<li>19: Frees the memories pointed by <code>contents</code>.</li>
<li>20: Returns <code>tv</code>, which is a pointer to the newly created
TfeTextView instance. If an error happens, NULL is returned.</li>
</ul>
<h2 id="save-related-functions">Save related functions</h2>
<p>Save and saveas functions write the contents in the GtkTextBuffer to
a file.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
<p>The function <code>tfe_text_view_save</code> writes the contents in
the GtkTextBuffer to a file specified by <code>tv-&gt;file</code>. If
<code>tv-&gt;file</code> is NULL, then it shows GtkFileChooserDialog and
prompts the user to choose a file to save. Then it saves the contents to
the file and sets <code>tv-&gt;file</code> to point the GFile instance
for the file.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
<p>The function <code>saveas</code> uses GtkFileChooserDialog and
prompts the user to select a existed file or specify a new file to save.
Then, the function changes <code>tv-&gt;file</code> and save the
contents to the specified file. If an error occurs, it is shown to the
user through the message dialog. The error is managed only in the
TfeTextView and no information is notified to the caller.</p>
<h3 id="save_file-function">save_file function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c save_file @@@</p>
<ul>
<li>The function <code>save_file</code> is called from
<code>saveas_dialog_response</code> and <code>tfe_text_view_save</code>.
This function saves the contents of the buffer to the file given as an
argument. If error happens, it displays an error message. So, a caller
of this function don’t need to take care of errors. The class of this
function is <code>static</code>. Therefore, only functions in this file
(<code>tfetextview.c</code>) call this function. Such static functions
usually don’t have <code>g_return_val_if_fail</code> function.</li>
<li>10-11: Gets the text contents from the buffer.</li>
<li>12: The function <code>g_file_replace_contents</code> writes the
contents to the file and returns the status (true = success/ false =
fail). It has many parameters, but some of them are almost always given
the same values.
<ul>
<li>GFile* file: GFile to which the contents are saved.</li>
<li>const char* contents: contents to be saved. The string is owned by
the caller.</li>
<li>gsize length: the length of the contents</li>
<li>const char* etag: entity tag. It is usually NULL.</li>
<li>gboolean make_backup: true to make a backup if the file exists.
false not to make it. the file will be overwritten.</li>
<li>GFileCreateFlags flags: usually <code>G_FILE_CREATE_NONE</code> is
fine.</li>
<li>char** new_etag: new entity tag. It is usually NULL.</li>
<li>GCancellable* cancellable: If a cancellable instance is set, the
other thread can cancel this operation. it is usually NULL.</li>
<li>GError** error: If error happens, GError will be set.</li>
</ul></li>
<li>13,14: If no error happens, set the modified flag to be FALSE. This
means that the buffer is not modified since it has been saved.</li>
<li>15-22: If it fails to save the contents, an error message will be
displayed.</li>
<li>17-18: Creates a message dialog. The parameters are:
<ul>
<li>GtkWindow* parent: transient parent window. This allows window
managers to keep the dialog on top of the parent window, or center the
dialog over the parent window. It is possible to give no parent window
to the dialog. However, it is encouraged to give parents to
dialogs.</li>
<li>GtkDialogFlags flags: GTK_DIALOG_MODAL for modal dialog. A modal
dialog is usually fine.</li>
<li>GtkMessageType type: GTK_MESSAGE_ERROR for error message. Other
options are GTK_MESSAGE_INFO, GTK_MESSAGE_WARNING and so on.</li>
<li>GtkButtonsType buttons: GTK_BUTTON_OK is used most often. Other
option is GTK_BUTTON_YES_NO, GTK_BUTTON_CANCEL and so on.</li>
<li>const gchar* message_format: gchar is the same as char. This format
is the same as printf. Arguments for the message format follow.</li>
</ul></li>
<li>19: Connects the “response” signal to
<code>gtk_window_destroy</code>, so that the dialog disappears when the
user clicks on the button.</li>
<li>20: Shows the message dialog.</li>
<li>21: Frees <code>err</code> with <code>g_error_free</code>
function.</li>
<li>23: Frees <code>contents</code>.</li>
<li>24: Returns to the caller.</li>
</ul>
<h3 id="saveas_dialog_response-function">saveas_dialog_response
function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c saveas_dialog_response @@@</p>
<ul>
<li>The function <code>saveas_dialog_response</code> is a signal handler
for the “response” signal on GtkFileChooserDialog. This handler analyzes
the response and determines whether to save the contents or not.</li>
<li>7-25: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user
has clicked on the <code>Save</code> button and the contents will be
saved.</li>
<li>8: Gets the GFile <code>file</code> from the
GtkFileChooserDialog.</li>
<li>9-10: If it doesn’t point GFile, a warning message will be output to
the log. This is not expected.</li>
<li>11: Otherwise, it calls <code>save_file</code> to save the contents
to the file.</li>
<li>12-22: If <code>save_file</code> has successfully saved the
contents, the following will be done.
<ul>
<li>If <code>tv-&gt;file</code> is GFile and <code>file</code> is a
different file, unref <code>tv-&gt;file</code>.</li>
<li>If <code>tv-&gt;file</code> is GFile and <code>file</code> points
the same file as <code>tv-&gt;file</code>, nothing needs to do.
Otherwise, <code>tv-&gt;file</code> is set to <code>file</code> and
“change-file” signal is emitted.</li>
</ul></li>
<li>22, 24: Unref <code>file</code>.</li>
<li>26: destroys the file chooser dialog.</li>
</ul>
<h3 id="tfe_text_view_save-function">tfe_text_view_save function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c tfe_text_view_save @@@</p>
<ul>
<li>The function <code>tfe_text_view_save</code> writes the contents to
the <code>tv-&gt;file</code> file. It calls
<code>tfe_text_view_saveas</code> or <code>save_file</code>.</li>
<li>1-3: The function is public, i.e. it is open to the other objects.
So, it doesn’t have <code>static</code> class. Public functions should
check the parameter type with <code>g_return_if_fail</code> function. If
<code>tv</code> is not a pointer to a TfeTextView instance, then it logs
an error message and immediately returns. This function is similar to
<code>g_return_val_if_fail</code>, but no value is returned because
<code>tfe_text_view_save</code> doesn’t return a value (void).</li>
<li>5-6: GtkTextBuffer <code>tb</code> and GtkWidget (GtkWindow)
<code>win</code> are set. The function
<code>gtk_widget_get_ancestor (widget, type)</code> returns the first
ancestor of the widget with type. The type is a GType. For example, the
type of GtkWindow is <code>GTK_TYPE_WINDOW</code> and the one of
TfeTextView is <code>TFE_TYPE_TEXT_VIEW</code>. Be careful. The
parent-child relationship here is the one for widgets, not classes. The
top level window may be a GtkApplicationWindow, but it depends on the
application. Because TfeTextView is a library, it can’t determine the
top level window type (GtkWindow or GtkApplicationWindow). GtkWindow is
a parent class of GtkApplication window so it is more general.
Therefore, TfeTextView takes GtkWindow as a top level window so that it
can be used by any application.</li>
<li>8-9: If the buffer hasn’t modified, it doesn’t need to be
saved.</li>
<li>10-11: If <code>tv-&gt;file</code> is NULL, which means no file has
given yet, it calls <code>tfe_text_view_saveas</code> to prompt a user
to select a file and save the contents.</li>
<li>12-13: If <code>tv-&gt;file</code> doesn’t point GFile, an error
message is logged out. It is not expected.</li>
<li>14-15: Otherwise, it calls <code>save_file</code> to save the
contents to the file <code>tv-&gt;file</code>.</li>
</ul>
<h3 id="tfe_text_view_saveas-function">tfe_text_view_saveas
function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c tfe_text_view_saveas @@@</p>
<ul>
<li>The function <code>tfe_text_view_saveas</code> shows
GtkFileChooserDialog and prompts the user to choose a file and save the
contents.</li>
<li>1-3: Check the type of <code>tv</code> because the caller may be
other object. This function is public.</li>
<li>6: GtkWidget <code>win</code> is set to the top level window.</li>
<li>8-11: Creates GtkFileChooserDialog. It has at least four parameters.
<ul>
<li>const char* title: title of the dialog shown at the bar.</li>
<li>GtkWindow* parent: transient parent window.</li>
<li>GtkFileChooserAction action: action is one of
<code>GTK_FILE_CHOOSER_ACTION_OPEN</code>,
<code>GTK_FILE_CHOOSER_ACTION_SAVE</code> and
<code>GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</code>. When you want to
save a file, <code>GTK_FILE_CHOOSER_ACTION_SAVE</code> is the
action.</li>
<li>const char* first_button_text: the label text of the first
button.</li>
<li>type: response ID for the sirst button. response ID is one of the
GtkReponseType. See <a
href="https://docs.gtk.org/gtk4/enum.ResponseType.html">GtkResponseType</a>.</li>
<li>followed by pairs of button text and response ID…</li>
<li>NULL is put at the end of the list.</li>
</ul></li>
<li>The GtkFileChooserDialog will have the title “Save file”, transient
parent <code>win</code>, save mode action, cancel and save button.</li>
<li>12: connects the “response” signal and
<code>saveas_dialog_response</code> handler.</li>
<li>13: Shows the dialog.</li>
</ul>
<p>When you use GtkFileChooserDialog, you need to divide the program
into two parts. One is a function which creates GtkFileChooserDialog and
the other is a signal handler. The function just creates and shows
GtkFileChooserDialog. The rest is done by the handler. It gets Gfile
from GtkFileChooserDialog and saves the buffer to the file by calling
<code>save_file</code>.</p>
<figure>
<img src="image/saveas.png" alt="Saveas process" />
<figcaption aria-hidden="true">Saveas process</figcaption>
</figure>
<h2 id="open-related-functions">Open related functions</h2>
<p>Open function shows GtkFileChooserDialog to a user and prompts them
to choose a file. Then it reads the file and puts the text into
GtkTextBuffer.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span></code></pre></div>
<p>The parameter <code>win</code> is the top-level window. It will be a
transient parent window of GtkFileChooserDialog when the dialog is
created.</p>
<p>This function may be called just after <code>tv</code> has been
created. In that case, <code>tv</code> has not been incorporated into
the widget hierarchy. Therefore it is impossible to get the top-level
window from <code>tv</code>. That’s why the function needs
<code>win</code> parameter.</p>
<p>This function is usually called when the buffer of <code>tv</code> is
empty. However, even if the buffer is not empty,
<code>tfe_text_view_open</code> doesn’t treat it as an error. If you
want to revert the buffer, calling this function is appropriate.</p>
<p>Open and read process is divided into two phases. One is showing
GtkFileChooserDialog and the other is its response handler. The response
handler gets the filename, reads the contents of the file and puts it
into the GtkTextBuffer.</p>
<h3 id="open_dialog_response-function">open_dialog_response
function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c open_dialog_response @@@</p>
<ul>
<li>2: The handler <code>open_dialog_response</code> has three
parameters.
<ul>
<li>GtkWidget *dialog: FileChooserDialog on which the “response” signal
is emitted.</li>
<li>gint response: response ID</li>
<li>TfeTextView *tv: textview to put the contents to</li>
</ul></li>
<li>10-11: If the response is not <code>GTK_RESPONSE_ACCEPT</code>, the
user has clicked on the “Cancel” button or close button on the header
bar. Then, “open-response” signal is emitted. The parameter of the
signal is <code>TFE_OPEN_RESPONSE_CANCEL</code>.</li>
<li>12-14: Gets the pointer to the GFile by
<code>gtk_file_chooser_get_file</code>. If it doesn’t point GFile, maybe
an error has occurred. It is not expected, though. Then it emits
“open-response” signal with the parameter
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
<li>15-22: If an error occurs when file reading, then it decreases the
reference count of the GFile, shows a message dialog to report the error
to the user and emits “open-response” signal with the parameter
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
<li>23-38: If the file has been successfully read, the following is
carried out. (It is not simple.)
<ul>
<li>the text is inserted to GtkTextBuffer</li>
<li>the temporary buffer <code>contents</code> is freed</li>
<li>modify-bit is set to FALSE</li>
<li>open-response signal is emitted with
<code>TFE_OPEN_REPONSE_SUCCESS</code></li>
<li>If the file are changed, change-file signal is emitted</li>
<li>If <code>tv-&gt;file</code> isn’t NULL,
<code>g_object_unref(tv-&gt;file)</code> is called</li>
<li><code>tv-&gt;file</code> is assigned with <code>file</code></li>
</ul></li>
<li>39: destroys GtkFileChooserDialog.</li>
</ul>
<h3 id="tfe_text_view_open-function">tfe_text_view_open function</h3>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c tfe_text_view_open @@@</p>
<ul>
<li>3-4: Check the type of the arguments <code>tv</code> and
<code>win</code>. Public functions always need to check the
arguments.</li>
<li>10-11: Creates GtkFileChooserDialog.
<ul>
<li>The title is “Open file”.</li>
<li>Transient parent window is the top-level window
<code>win</code>.</li>
<li>The action is open mode.</li>
<li>The buttons are Cancel and Open.</li>
</ul></li>
<li>12: connects the “response” signal and the handler.</li>
<li>13: Shows the dialog.</li>
</ul>
<p>The whole process between the caller and TfeTextView is shown in the
following diagram. It is really complicated. Because signal is the only
way for GtkFileChooserDialog to communicate with others.</p>
<figure>
<img src="image/open.png" alt="Caller and TfeTextView" />
<figcaption aria-hidden="true">Caller and TfeTextView</figcaption>
</figure>
<ol type="1">
<li>A caller gets a pointer <code>tv</code> to a TfeTextView instance by
calling <code>tfe_text_view_new</code>.</li>
<li>The caller connects the handler (left bottom in the diagram) and the
signal “open-response”.</li>
<li>It calls <code>tfe_text_view_open</code> to prompt the user to
select a file from GtkFileChooserDialog.</li>
<li>The dialog emits a signal and it invokes the handler
<code>open_dialog_response</code>.</li>
<li>The handler reads the file and inserts the text into GtkTextBuffer
and emits a signal to inform the status as a response code.</li>
<li>The handler out of the TfeTextView receives the signal.</li>
</ol>
<h2 id="getting-gfile-in-tfetextview">Getting GFile in TfeTextView</h2>
<p>You can get the GFile in a TfeTextView instance with
<code>tfe_text_view_get_file</code>. It is very simple.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfetextview/tfetextview.c tfe_text_view_get_file @@@</p>
<p>The important thing is to duplicate <code>tv-&gt;file</code>.
Otherwise, if the caller frees the GFile object,
<code>tv-&gt;file</code> is no more guaranteed to point the GFile.
Another reason to use <code>g_file_dup</code> is that GFile isn’t
thread-safe. If you use GFile in the different thread, the duplication
is necessary. See <a
href="https://docs.gtk.org/gio/method.File.dup.html">Gio API Reference –
g_file_dup</a>.</p>
<h2 id="the-api-document-and-source-file-of-tfetextview.c">The API
document and source file of tfetextview.c</h2>
<p>Refer <a
href="https://toshiocp.github.io/Gtk4-tutorial/tfetextview_doc.html">API
document of TfeTextView</a>. The markdown file is under the directory
<code>src/tfetextview</code>.</p>
<p>You can find all the TfeTextView source codes under src/tfetextview
directories.</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
  </html>
